#include "programmer.h"
#include "globals.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <avr/io.h>
#include <avr/wdt.h>
//#include <avr/boot.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <util/crc16.h>
#include "layer_comm.h"
#include "layer_uart.h"
#include "bikirom-ints.h"
#include "Timers.h"
#include "Convert.h"
#include "adc.h"
#include "bikirom-timers.h"

//------------------------------------------------------------------------------
// Macro's/Defines
#define CONSULT_BUFFER_LEN		0x10
#define CONSULT_UART			3


//------------------------------------------------------------------------------
// Type Definitions
typedef struct _typAdcData
{
	unsigned char	nHeader;
	unsigned short	nVref;
	unsigned short	nAux1;
	unsigned short	nAux2;
	unsigned short	nAux3;
	unsigned short	nAux4;
	unsigned short	nConsultClks;
} _typAdcData;

//------------------------------------------------------------------------------
//	Static Definitions
static unsigned char consult_rx[CONSULT_BUFFER_LEN];
static unsigned char consult_tx[CONSULT_BUFFER_LEN];

//------------------------------------------------------------------------------
//	Global Definitions
struct _typGlobalData	mGlobalData;

//------------------------------------------------------------------------------
// Private Functions
void test_read_pattern(void);

void consult_loopback(unsigned char c, unsigned short nLength)
{
	uart_send_byte(CONSULT_UART, c);
}

void tmrGetAdc(void* typTimer)
{
	adc_start_sequence();
}

void tmrTestComms(void* typTimer)
{
	comm_response(eCmdRespGo);
}

//-------------------------------------------------------------------------------------
// Consult Clock latching
void tmr_consult_latch(void* typTimer)
{
	mGlobalData.nConsultClks = mGlobalData.nConsultClksTmp;
}

void consult_clock_overflow(void)
{
	if (mGlobalData.nConsultClksTmp != 0xFF)
	{
		mGlobalData.nConsultClksTmp++;
	}
}

//-------------------------------------------------------------------------------------

void initialise(void)
{
	// Initialise Comm
	comm_init(&comm_usb_handler);

	// Initialise Uart loopback
	uart_init(CONSULT_UART,9600,&consult_loopback,
		consult_tx, CONSULT_BUFFER_LEN,
		consult_rx, CONSULT_BUFFER_LEN,
		eIntNone);

	// Initialise the special timer
	timer_initialise_mode_exact(0,TIMER_CLK_DIV1024,0xFF,TIMER_CTC);

	// Initialise the thread timers
	btimer_init(100, 2, eTimer2_CompareA);

	// Initialise Timers
	btimer_add(&tmrGetAdc ,0 ,20 ,0	,TRUE ,3);
	//btimer_add(&tmrTestComms ,0 ,1000 ,0	,TRUE ,3);

	// Initialise the CLK Capture timer
	mGlobalData.nConsultClks = 0;
	mGlobalData.nConsultClksTmp = 0;
	timer_initialise_mode_exact(4, TIMER_CLK_T_FALL, 1152,TIMER_NORMAL);
	timer_attach(eTimer4_Overflow, &consult_clock_overflow);

	// Setup IO Ports
	// OE2 High
	ssbi(DDR_OE2);
	ssbi(PORT_OE2);
	// OE1 High, Lo to read
	ssbi(DDR_OE1);
	ssbi(PORT_OE1);
	// CE Lo to read
	ssbi(DDR_CE);
	ccbi(PORT_CE);
	// LE High to not latch
	ssbi(DDR_LE);
	ssbi(PORT_LE);

}

__attribute__((noreturn)) int main(void) 
{
	initialise();

	comm_response(eCmdRespBoot);

	for ( ; ; );
}

void test_get_prog_adc(void)
{
	_typAdcData	typAdcData;

	typAdcData.nHeader = ID_GENERAL_SPECIFIC;
	typAdcData.nAux1 = mGlobalData.AdInputs[ADC_AUX1].Raw;
	typAdcData.nAux2 = mGlobalData.AdInputs[ADC_AUX2].Raw;
	typAdcData.nAux3 = mGlobalData.AdInputs[ADC_AUX3].Raw;
	typAdcData.nAux4 = mGlobalData.AdInputs[ADC_AUX4].Raw;
	typAdcData.nVref = mGlobalData.AdInputs[ADC_VREF].Raw;
	typAdcData.nConsultClks = mGlobalData.nConsultClks;

	comm_new(eMsgPc, eMsgAtmel, sizeof(typAdcData));
	comm_append((uint8_t*)&typAdcData,sizeof(typAdcData));
	comm_send();
}

void	board_read	(uint16_t Address, uint8_t *Buffer, uint16_t Length)
{
	uint16_t nLoop;

	ADDRESS_OE;

	// Inputs
	outb(DDR_BU1,0x00);
	outb(DDR_BU2,0x00);

	// OE2 High
	ssbi(DDR_OE2);
	ssbi(PORT_OE2);
	// OE1 High, Lo to read
	ssbi(DDR_OE1);
	ssbi(PORT_OE1);
	// CE Lo to read
	ssbi(DDR_CE);
	ccbi(PORT_CE);

	for (nLoop = 0; nLoop<Length; nLoop +=2)
	{
		ADDRESS(Address);					// Output Address

		ccbi(PORT_OE1);						// OE Low

		Buffer[nLoop] = inb(PIN_BU1);		// Read Data
		Buffer[nLoop+1] = inb(PIN_BU2);

		ssbi(PORT_OE1);						// OE High
		
		Address += 2;
	}

	ssbi(PORT_OE1);
	ssbi(PORT_OE2);
	ssbi(PORT_CE);

	ADDRESS_OD;
}

void test_read_pattern(void)
{
	uint32_t	nAdr;
	uint32_t	nRead;
	uint8_t		bStatus = TRUE;

	for (nAdr = 0x8000; nAdr < 0x10000 && bStatus; nAdr += 2)
	{
		board_read(nAdr, (uint8_t*)&nRead,4);

		if (nAdr != nRead)
		{
			bStatus = FALSE;
		}
	}

	if (bStatus)
	{
		comm_response(eCmdRespPassed);
	}
	else
	{
		comm_response(eCmdRespNotPassed);
	}
}

void comm_usb_handler(volatile unsigned char* buffer, unsigned short nLength)
{
	volatile unsigned char *pchBuf = &buffer[6];

	if (comm_to(buffer) == eMsgAtmel)
	{
		switch (*pchBuf++)
		{
		case eMsgIdRunCommand:
			switch (*pchBuf++)
			{
			case eCmdRunGetProgADC:
				test_get_prog_adc();
				break;

			case eCmdRunTestReadPattern:
				test_read_pattern();
				break;

			case eCmdRunDUTEnable:
				ssbi(DDR_DUT_EN);
				ccbi(PORT_DUT_EN);
				comm_response(eCmdRespPassed);
				break;

			case eCmdRunDUTDisable:
				ssbi(DDR_DUT_EN);
				ssbi(PORT_DUT_EN);
				comm_response(eCmdRespPassed);
				break;

			default:
				comm_response(eCmdRespUnknown);
				break;

			}
			break;

		default:
			comm_response(eCmdRespUnknown);
			break;
		}
	}
	else
	{
		comm_response(eCmdRespUnknownReciever);
	}
}


